﻿using Admin.NET.Application.Entity;
using Admin.NET.Core.Service;
using Furion.DataEncryption;
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Admin.NET.Application.Service
{
    [ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
    public class MemberAuthService : IDynamicApiController, ITransient
    {
        private readonly UserManager _userManager;
        private readonly SqlSugarRepository<LoveMembers> _loveMembersRepository;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly SysConfigService _sysConfigService;
        private readonly ICaptcha _captcha;
        private readonly SysCacheService _sysCacheService;
        public MemberAuthService(
            UserManager userManager,
            SqlSugarRepository<LoveMembers> loveMembersRepository,
            IHttpContextAccessor httpContextAccessor,
            SysConfigService sysConfigService,
            ICaptcha captcha,
            SysCacheService sysCacheService
            )
        {
            _userManager = userManager;
            _loveMembersRepository = loveMembersRepository;
            _httpContextAccessor = httpContextAccessor;
            _sysConfigService = sysConfigService;
            _captcha = captcha;
            _sysCacheService = sysCacheService;
        }

        /// <summary>
        /// 账号密码登录 🔖
        /// </summary>
        /// <param name="input"></param>
        /// <remarks>用户名/密码：superadmin/123456</remarks>
        /// <returns></returns>
        [AllowAnonymous]
        [DisplayName("账号密码登录")]
        public virtual async Task<LoginOutput> Login([Required] LoginInput input)
        {
            // 判断密码错误次数（缓存30分钟）
            string keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{"Love_"}{input.Account}";
            int passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
            var passwordMaxErrorTimes = 10;// await _sysConfigService.GetConfigValue<int>(ConfigConst.SysPasswordMaxErrorTimes);
            // 若未配置或误配置为0、负数, 则默认密码错误次数最大为10次
            if (passwordMaxErrorTimes < 1)
                passwordMaxErrorTimes = 10;
            if (passwordErrorTimes > passwordMaxErrorTimes)
                throw Oops.Oh(ErrorCodeEnum.D1027);

            // 是否开启验证码
            if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysCaptcha))
            {
                // 判断验证码
                if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
                    throw Oops.Oh(ErrorCodeEnum.D0008);
            }
            // 账号是否存在
            var user = await _loveMembersRepository.AsQueryable().ClearFilter().FirstAsync(u => u.Account.Equals(input.Account));
            _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);

            // 账号是否被冻结
            if (user.Status == StatusEnum.Disable)
                throw Oops.Oh(ErrorCodeEnum.D1017);

            // 国密SM2解密（前端密码传输SM2加密后的）
            //try
            //{
            //    input.Password = CryptogramUtil.SM2Decrypt(input.Password);
            //}
            //catch
            //{
            //    throw Oops.Oh(ErrorCodeEnum.D0010);
            //}

            VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user);

            // 登录成功则清空密码错误次数
            _sysCacheService.Remove(keyPasswordErrorTimes);

            return await CreateToken(user);
        }

        /// <summary>
        /// 生成Token令牌 🔖
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        [NonAction]
        internal virtual async Task<LoginOutput> CreateToken(LoveMembers user)
        {
            // 单用户登录
            // await _sysOnlineUserService.SingleLogin(user.Id);

            // 生成Token令牌
            var tokenExpire = await _sysConfigService.GetTokenExpire();
            var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
            {
                { ClaimConst.UserId, user.Id },
                { ClaimConst.Account, user.Account },
                { ClaimConst.RealName, user.RealName },
                { ClaimConst.AccountType, user.AccountType },
            }, tokenExpire);

            // 生成刷新Token令牌
            var refreshTokenExpire = await _sysConfigService.GetRefreshTokenExpire();
            var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);

            // 设置响应报文头
            _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);

            // Swagger Knife4UI-AfterScript登录脚本
            // ke.global.setAllHeader('Authorization', 'Bearer ' + ke.response.headers['access-token']);

            // 更新用户登录信息
            user.LastLoginIp = _httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(true);
            (user.LastLoginAddress, double? longitude, double? latitude) = CommonUtil.GetIpAddress(user.LastLoginIp);
            user.LastLoginTime = DateTime.Now;
            user.LastLoginDevice = CommonUtil.GetClientDeviceInfo(_httpContextAccessor.HttpContext?.Request?.Headers?.UserAgent);
            await _loveMembersRepository.AsUpdateable(user).UpdateColumns(u => new
            {
                u.LastLoginIp,
                u.LastLoginAddress,
                u.LastLoginTime,
                u.LastLoginDevice,
            }).ExecuteCommandAsync();

            return new LoginOutput
            {
                AccessToken = accessToken,
                RefreshToken = refreshToken
            };
        }

        /// <summary>
        /// 验证用户密码
        /// </summary>
        /// <param name="input"></param>
        /// <param name="keyPasswordErrorTimes"></param>
        /// <param name="passwordErrorTimes"></param>
        /// <param name="user"></param>
        private void VerifyPassword(LoginInput input, string keyPasswordErrorTimes, int passwordErrorTimes, LoveMembers user)
        {
            if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
            {
                if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
                {
                    _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                    throw Oops.Oh(ErrorCodeEnum.D1000);
                }
            }
            else
            {
                if (!CryptogramUtil.Decrypt(user.Password).Equals(input.Password))
                {
                    _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                    throw Oops.Oh(ErrorCodeEnum.D1000);
                }
            }
        }

        /// <summary>
        /// 获取登录账号 🔖
        /// </summary>
        /// <returns></returns>
        [DisplayName("获取登录账号")]
        public virtual async Task<LoginUserOutput> GetUserInfo()
        {
            var user = await _loveMembersRepository.GetFirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D1011).StatusCode(401);
            // 获取水印文字（若系统水印为空则全局为空）
            var watermarkText = await _sysConfigService.GetConfigValue<string>(ConfigConst.SysWebWatermark);
            if (!string.IsNullOrWhiteSpace(watermarkText))
                watermarkText += $"-{user.RealName}"; // $"-{user.RealName}-{_httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(true)}-{DateTime.Now}";
            return new LoginUserOutput
            {
                Id = user.Id,
                Account = user.Account,
                RealName = user.RealName,
                Phone = user.Phone,
                IdCardNum = user.IdCardNum,
                Email = user.Email,
                AccountType = user.AccountType,
                Avatar = user.Avatar,
                Address = user.Address,
                WatermarkText = watermarkText
            };
        }

    }
}